var LockCrypt = {
    prefs: null,
    lang : null,
    timeout : null,
    onlyEmpty : false,
    autoFill : true,
    enabled : true,
    enableSave : true,
    running : false,
    timeoutValue : 1000, 
    filledForms : [], // array of filled data, used to detect changes
    formSubmitted : null,
    excludeList : [],
    
    /** 
      * Loads preferences and assigns listeners if LockCrypt is enabled 
      */
    onLoad : function () {
        LockCrypt.loadPrefs();
        var appcontent = document.getElementById("appcontent");
        if (appcontent) {
            appcontent.addEventListener("DOMContentLoaded", LockCrypt.LockCryptInit, true);
        }
    },
    
    /**
      * Loads preferences from storage 
      */
    loadPrefs : function () {
        this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
            .getService(Components.interfaces.nsIPrefService)
            .getBranch("LockCrypt.");
        this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
        this.prefs.addObserver("", this, false);

        this.autoFill = this.prefs.getBoolPref("autoFill");
        this.enabled = this.prefs.getBoolPref("enabled");
        this.enableSave = this.prefs.getBoolPref("enableSave");
        this.timeoutValue = this.prefs.getIntPref("timeoutValue");
        this.onlyEmpty = this.prefs.getBoolPref("onlyEmpty");
        var exclude = this.prefs.getCharPref("excludeList");
    	if(exclude.length > 0) {
	        this.excludeList = JSON.parse(this.prefs.getCharPref("excludeList"));
        }
        var gBundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
        this.lang = gBundle.createBundle("chrome://LockCrypt/locale/LockCrypt.properties");
    },
    
    /**
      * Assigns submit listeners and autofills 
      */
    LockCryptInit : function (e) {
        LockCrypt.running = false;
        var listener = {
            finished : function (data) {
                if (data !== null && typeof(data) !== 'undefined' && data === 'PONG') {
                    LockCrypt.running = true;
                }
            }
        };
        var data = {
            "type" : "PING"
        };
        LockCrypt.timeout = setTimeout(LockCrypt.initIfRunning, 2000);
        LockCrypt.sendReceive('localhost',1556,JSON.stringify(data),listener);
    },
    
    /**
      * Enables the extension only if LockCrypt is running
      */
    initIfRunning : function () {
        var statusIcon = document.getElementById("lc-statusbar");
        if (LockCrypt.running) {
            if (LockCrypt.enabled) {
                if (!LockCrypt.currentSiteIsExcluded()) {
                    if (LockCrypt.enableSave) {
                        LockCrypt.assignSubmitListeners();
                    }
                    if (LockCrypt.autoFill) {
                        LockCrypt.autoFillForms(); 
                    }
                    dump("LockCrypt loaded\n");
                } else {
                    dump("LockCrypt loaded - site excluded\n");
                }
                statusIcon.setAttribute("src", "chrome://LockCrypt/skin/classic/LockCrypt.ico");
                statusIcon.setAttribute("tooltiptext", "LockCrypt");
            } else {
                dump("LockCrypt disabled\n");
                statusIcon.setAttribute("src", "chrome://LockCrypt/skin/classic/LockCrypt_Disabled.ico");
                statusIcon.setAttribute("tooltiptext", "LockCrypt");
            }
        } else {
            dump("LockCrypt not running\n");
            statusIcon.setAttribute("src", "chrome://LockCrypt/skin/classic/LockCrypt_Disabled.ico");
            statusIcon.setAttribute("tooltiptext", "LockCrypt " + LockCrypt.getString("notRunning"));
        }
    },
    
    /**
      * Removes the preferences listener when the window is closed 
      */
    onUnload : function () {
        this.prefs.removeObserver("", this);
    },
    
    /**
      * Listens for changes to preferences
      */
	  observe: function (subject, topic, data) {
    		if (topic !== "nsPref:changed") {
    			return;
    		}
    		switch(data) {
    		    case "enabled":
                    this.enabled = this.prefs.getBoolPref("enabled");
                    var statusIcon = document.getElementById("lc-statusbar");
                    if (LockCrypt.enabled) {
                        if (LockCrypt.enableSave) {
                            LockCrypt.assignSubmitListeners();
                        }
                        if (this.autoFill) {
                            LockCrypt.autoFillForms(); 
                        }
                        dump("LockCrypt loaded\n");
                        statusIcon.setAttribute("src", "chrome://LockCrypt/skin/classic/LockCrypt.ico");
                    } else {
                        statusIcon.setAttribute("src", "chrome://LockCrypt/skin/classic/LockCrypt_Disabled.ico");
                    }
    				break;
    			case "autoFill":
    				this.autoFill = this.prefs.getBoolPref("autoFill");
    				break;
    			case "timeoutValue":
    				this.timeoutValue = this.prefs.getIntPref("timeoutValue");
    				break;
    			case "onlyEmpty":
    				this.onlyEmpty = this.prefs.getBoolPref("onlyEmpty");
    				break;
    			case "excludeList":
    				this.excludeList = JSON.parse(this.prefs.getCharPref("excludeList"));
    				break;
    			case "enableSave":
    				this.enableSave = this.prefs.getBoolPref("enableSave");
    				if (this.enableSave) {
    				    LockCrypt.assignSubmitListeners();
    				} else {
    				    LockCrypt.removeSubmitListeners();
    				}
    				break;
    				
    		}
    },
    
    /**
      * Fills all matching fields in the current page with the specified form data 
      * @param formData The JSON response from LockCrypt containing match data
      * @param onlyEmpty True if only empty fields should be filled, otherwise false
      */
    fillForm : function (formData, onlyEmpty) {
        if (typeof onlyEmpty === 'undefined') {
            onlyEmpty = LockCrypt.onlyEmpty;
        }
        var forms = content.document.forms;
        var i,j,k, thisForm, formName;
        if ((typeof formData.usernameID) === 'undefined') { // form automatically created, match label names
            for(i=0;i<forms.length;i++) {
                thisForm = forms[i];
                var elements = thisForm.getElementsByTagName('input');
                for(j=0;j<elements.length;j++) { // every element in the form
                    var thisElement = elements[j];
                    if (thisElement.type.toLowerCase() === 'text' || thisElement.type.toLowerCase() === 'password') {
                        if ((onlyEmpty && thisElement.value.length === 0) || !onlyEmpty) {
                            var filled = false;
                            for(k=0;k<formData.fields.length && !filled;k++) { // check each saved field in the account
                                var thisField = formData.fields[k];
                                if (thisElement.name.match(new RegExp(thisField.name,"i")) || LockCrypt.findLabel(thisForm, thisElement) === decodeURIComponent(thisField.name)) {
                                    var decodedVal = decodeURIComponent(thisField.value);
                                    LockCrypt.fillElement(thisForm, thisElement, decodedVal);
                                    filled = true;
                                }
                            }
                        }
                    }
                }
            }
        } else { // account has been automatically created or exported
            var username = decodeURIComponent(formData.username);
            var usernameID = decodeURIComponent(formData.usernameID);
            var password = decodeURIComponent(formData.password);
            var passwordID = decodeURIComponent(formData.passwordID);
            var matchingUserIDs = content.document.getElementsByName(usernameID);
            var matchingPasswordIDs = content.document.getElementsByName(passwordID);
            for(i=0;i<forms.length;i++) {
                thisForm = forms[i];
                if (username.length > 0) {
                    for(j=0;j<matchingUserIDs.length;j++) { // fill all matching textboxes in the DOM matching the username field
                                        dump("6\n");
                        if (onlyEmpty) {
                            if (matchingUserIDs[j].value.length === 0) {
                                LockCrypt.fillElement(thisForm, matchingUserIDs[j], username);
                            }
                        } else {
                            LockCrypt.fillElement(thisForm, matchingUserIDs[j], username);
                        }
                    }
                }
                if (password.length > 0) {
                    for(j=0;j<matchingPasswordIDs.length;j++) { // fill all matching textboxes in the DOM matching the password field
                        if (onlyEmpty) {
                            if (matchingPasswordIDs[j].value.length === 0) {
                                LockCrypt.fillElement(thisForm, matchingPasswordIDs[j], password);
                            }
                        } else {
                            LockCrypt.fillElement(thisForm, matchingPasswordIDs[j], password);
                        }
                    }
                }
            }
        }
    },
    
    /**
      * Fills all forms on the page. If more than one match is found, prompts the user for the account to use 
      */
    fillForms : function () {
        if (LockCrypt.enabled) {
            var listener = {
                finished : function (data) {
                    var responseData = JSON.parse(data);
                    //dump(data);
                    if (typeof responseData.error !== 'undefined') {
                        alert(decodeURIComponent(responseData.error));
                    } else {
                        var matchData = responseData.matches;
                        if (matchData.length > 0) {
                            var params = {
                                inparams : matchData,
                                out : null
                            };
                            window.openDialog("chrome://LockCrypt/content/promptfill.xul", "promptfill", "modal,centerscreen,chrome", params);
                            var result = params.out;
                            if (result !== null) { //dialog not cancelled
                                var selectedAccount = matchData[result.accountNum];
                                LockCrypt.fillForm(selectedAccount, result.onlyEmpty);
                            }
                        } else {
                            alert(LockCrypt.getString("err_NoAccounts"));
                        }
                    }
                }
            };
            var data = {
                "type" : "REQUEST",
                "URL" : window.content.location.href
            };
            this.sendReceive('localhost',1556,JSON.stringify(data),listener);
        }
    },
    
    /**
      * Fills the specified element on the specified form and saves it's value to detect changes and re-prompt to save.
      */
    fillElement : function (form, element, value) {
                        dump("fillemenent\n");
        var formId = LockCrypt.getFormId(form);
    	if (typeof LockCrypt.filledForms[formId] === 'undefined') {
    	    LockCrypt.filledForms[formId] = [];
    	}
    	LockCrypt.filledForms[formId][element.name] = value;
    	element.value = value;
    },
    
    /**
      * Fills all forms on the document with the first available match 
      */
    autoFillForms : function () {
        var listener = {
            finished : function (data) {
                var responseData = JSON.parse(data);
                //dump(data);
                if (typeof responseData.error !== 'undefined') {
                    alert(decodeURIComponent(responseData.error));
                } else {
                    var matchData = responseData.matches;
                    for(var i=0;i<matchData.length;i++) { // loop through each saved form info
                        var savedForm = matchData[i];
                        LockCrypt.fillForm(savedForm);
                    }
                }
            }
        };
        var data = {
            "type" : "REQUEST",
            "URL" : window.content.location.href
        };
        if (LockCrypt.enabled) {
            this.sendReceive('localhost',1556,JSON.stringify(data),listener);
        }
    },
    
    /**
      * Assigns listeners to listen for form submissions 
      */
    assignSubmitListeners : function () {
        var forms = content.document.forms;
        for(var i=0;i<forms.length;i++) { // add a submit listener to each form
            var thisForm = forms[i];
            var elements = thisForm.elements;
            var listenerAdded = false;
            var thisElement;
            for(var j=0;j<elements.length;j++) {
                thisElement = elements[j];
                if (thisElement.tagName.toLowerCase() === 'input') {
                    if (thisElement.name.match(/username/i) || thisElement.name === 'uname') {
                        if (!listenerAdded) {
                            listenerAdded = true;
                            thisForm.addEventListener("submit", LockCrypt.doSave, true );
                            dump("Detected username - added submit listener to " +thisForm.name+ "\n");
                        }
                    } else if (thisElement.name.match(/pass/i) || thisElement.name === 'pword') {
                        if (!listenerAdded) {
                            listenerAdded = true;
                            thisForm.addEventListener("submit", LockCrypt.doSave, true );
                            dump("Detected password - added submit listener to " +thisForm.name+ "\n");
                        }
                    }
                }
            }
        }
    },
    
    /**
      * Removes listeners to listen for form submissions 
      */
    removeSubmitListeners : function () {
        var forms = content.document.forms;
        for(var i=0;i<forms.length;i++) {
            try {
                forms[i].removeEventListener("submit", LockCrypt.doSave, true);
            } catch(ignore) { }
        }
    },
    
    /**
      * Tries to find a 'submit' button which can be clicked on the specified form, 
      * uses the standard .submit() method if it can't find one
      * @param frm A reference to the form to save
      */
    submitForm : function (frm) {
        LockCrypt.removeSubmitListeners();
        var elements = frm.elements;
        var thisElement;
        for(var j=0;j<elements.length;j++) {
            thisElement = elements[j];
            if (typeof(thisElement.type) !== 'undefined' && thisElement.type.toLowerCase() === 'submit') {
                thisElement.click();
                return;
            }
        }
        frm.submit();
    },
    
    /**
      * Handles the form submit event and calls checkDetails() 
      * Adds a timer to submit the form if no response is received from LockCrypt
      */
    doSave : function (e) {
        LockCrypt.formSubmitted = e.originalTarget;
        // Form filling uses an ASync socket, so if we submit straight away, the data will be gone before the form is saved
        // If we cancel the submit and resubmit in the listener, the form will never submit if there's no response from LockCrypt
        if (LockCrypt.enabled) {
            e.preventDefault();
            LockCrypt.timeout = setTimeout(LockCrypt.promptTimeoutSubmit, LockCrypt.timeoutValue);
            LockCrypt.checkDetails(LockCrypt.formSubmitted);
        }
    },
    
    /**
      * Retrieves a localized string
      * @param name The name of the string to lookup
      * @return The localized string
      */
    getString : function (name) {
        var bundle = document.getElementById('lc-bundle');
        return bundle.getString(name);
    },

    /**
      * Retrieves a formatted localized string
      * @param name The name of the string to lookup
      * @param formatArray The array to use to replace values in the string
      * @return The localized string
      */    
    getFormattedString : function (name, formatArray) {
        var bundle = document.getElementById('lc-bundle');
        return bundle.getFormattedString(name, formatArray);
    },
    
    /**
      * Called when a submit timer expires. Assumes LockCrypt is not running and prompts the user
      * to submit the form anyway or continue waiting.
      */
    promptTimeoutSubmit : function () {
        var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                .getService(Components.interfaces.nsIPromptService);
        var flags = prompts.STD_YES_NO_BUTTONS;
        var submitAnyway = prompts.confirmEx(window, LockCrypt.getString("information"), LockCrypt.getFormattedString("err_timeout",[LockCrypt.timeoutValue]), flags, 
                                             null,null,null,null,{}) !== 0 ? true : false;
        if (submitAnyway) {
            LockCrypt.submitForm(LockCrypt.formSubmitted);
        }
    },
    
    /**
      * Checks the data the user entered is the same as the account, prompts to re-save otherwise 
      * @param form A reference to the form to check
      */
    checkDetails : function (thisForm) {
        var listener = {
            finished : function (data) {
                if (LockCrypt.timeout !== null) { // cancel the submit timer, we know LockCrypt is running
                    window.clearTimeout(LockCrypt.timeout);
                    LockCrypt.timeout = null;
                }
                var responseData = JSON.parse(data);
                var formId = LockCrypt.getFormId(thisForm);
                var elements = thisForm.getElementsByTagName('input');
                if (typeof responseData.error !== 'undefined') {
                    alert(decodeURIComponent(responseData.error)); // error checking form, just submit it
                    LockCrypt.submitForm(thisForm);
                } else {
                    var matchData = responseData.matches;
                    if (matchData.length > 0) { // forms already saved for this URL or we filled this form, check to see if it's changed
                        if (typeof LockCrypt.filledForms[formId] !== 'undefined') {
                            for(var i=0; i<matchData.length; i++) { // loop through each saved account
                                var formData = matchData[i];
                                for(var j=0; j<elements.length; j++) { // every element in the form
                                    var thisElement = elements[j];
                                    if (thisElement.type.toLowerCase() === 'text' || thisElement.type.toLowerCase() === 'password') {
                                        var savedName = LockCrypt.filledForms[formId][thisElement.name];
                                        if (typeof savedName !== 'undefined' && savedName !== thisElement.value) {
                                            LockCrypt.promptSaveByRef(thisForm, true, true);
                                            return;
                                        }
                                    }
                                }
                            }
                            LockCrypt.submitForm(thisForm);
                        } else {
                            LockCrypt.saveIfNotExists(thisForm); // check if the account exists, or prompt to save
                        }
                    } else {
                        LockCrypt.promptSaveByRef(thisForm, false, true); // no matches for this URL, prompt to save
                    }
                }
            }
        };
        var data = {
            "type" : "REQUEST",
            "URL" : window.content.location.href
        };
        this.sendReceive('localhost',1556,JSON.stringify(data),listener);
    },
    
    /**
      * Prompts to save the specified form unless an exact match exists in LockCrypt
      * @param form A reference to the form to save
      */
    saveIfNotExists : function (form) {
        var listener = {
            finished : function (data) {
                if (LockCrypt.timeout !== null) { // cancel the submit timer, we know LockCrypt is running
                    window.clearTimeout(LockCrypt.timeout);
                    LockCrypt.timeout = null;
                }
                var responseData = JSON.parse(data);
                if (typeof responseData.error !== 'undefined') {
                    alert(decodeURIComponent(responseData.error)); // error checking form, just submit it
                    LockCrypt.submitForm(form);
                } else {
                    if (responseData.exists) { // form already saved in LockCrypt
                        LockCrypt.submitForm(form);
                    } else { // not saved, prompt
                        LockCrypt.promptSaveByRef(form, false, true);
                    }
                }
            }
        };
        var fields = [];
        var inputs = form.getElementsByTagName("input");
        for(var i=0;i<inputs.length;i++) {
            var element = inputs[i];
            if (element.type.toLowerCase() === 'text' || element.type.toLowerCase() === 'password') {
                fields[fields.length] = {
                    name : LockCrypt.findLabel(form, element),
                    value : element.value
                };
            }
        }
        if (fields.length > 0) {
            var data = {
                "type" : "CHECKIFEXISTS",
                "URL" : window.content.location.href,
                "fields" : fields
            };
            this.sendReceive('localhost',1556,JSON.stringify(data),listener);
        } else {
            LockCrypt.submitForm(form);
        }
    },
    
    /**
      * Prompts the user the save the form from the name of the form
      * @param formName The name of the form to save
      * @param changed True if the form has been changed and is being re-saved, otherwise false.
      *                Determines the message shown when saving.
      */
    promptSaveByName : function (formName, changed) {
        var formSubmitted = LockCrypt.getFormByName(formName);
        if(formSubmitted === null) {
            alert(LockCrypt.getString("formSaveFail"));
        } else {
            LockCrypt.promptSaveByRef(formSubmitted, changed, true);
        }
    },
    
    /**
      * Prompts the user the save the form from a reference to the form
      * @param formName The name of the form to save
      * @param changed True if the form has been changed and is being re-saved, otherwise false.
      *                Determines the message shown when saving.
      * @param submitAfter True if the form should be submitted after saving, otherwise false
      */
    promptSaveByRef : function(form, changed, submitAfter) {
        var listener = {
            finished : function (data) {
                var responseData = JSON.parse(data);
                if (typeof responseData.error !== 'undefined') {
                    alert(decodeURIComponent(responseData.error));
                    if(submitAfter) {
                        LockCrypt.submitForm(form);
                    }
                } else {
                    var extraFieldsAry = [];
                    var inputs = form.getElementsByTagName("input");
                    for(var i=0;i<inputs.length;i++) {
                        var element = inputs[i];
                        if (element.type.toLowerCase() === 'text' || element.type.toLowerCase() === 'password') {
                            extraFieldsAry[extraFieldsAry.length] = {
                                name : LockCrypt.findLabel(form, element),
                                value : element.value,
                                include : element.value.length > 0 ? true : false
                            };
                        }
                    }
                    if (extraFieldsAry.length === 0) {
                        alert(LockCrypt.getString('err_NoFields'));
                    } else {
                        var params = {
                            inparams : {
                                url : window.content.location.href,
                                isChanged : changed,
                                existing : responseData.existing,
                                suggested : decodeURIComponent(responseData.suggested),
                                fields : extraFieldsAry
                            },
                            out : null
                        };
                        window.openDialog("chrome://LockCrypt/content/promptsave.xul", "promptsave", "modal,centerscreen,chrome", params);
                        if (params.out !== null) {
                            LockCrypt.saveFormByRef(form, params.out.name, params.out.fields, params.out.thisURL, true);
                        } else { // dialog cancelled
                            if(submitAfter) {
                                LockCrypt.submitForm(form);
                            }
                        }
                    }
                }
            }
        };
        var data = {
            "type" : "NAMEENQUIRY",
            "URL" : window.content.location.href
        };
        this.sendReceive('localhost',1556,JSON.stringify(data),listener);
    },
    
    /**
      * Saves a form from the form name
      * @param formName The name of the form to save
      * @param accountName The name to save the account as
      * @param fields A JSON object containing field data
      * @param currentURLOnly True if the account should only apply to the current URL, otherwise false
      */
    saveFormByName : function (formName, accountName, fields, currentURLOnly) {
        var thisForm = LockCrypt.getFormByName(formName);
        LockCrypt.saveFormByRef(thisForm, accountName, fields, currentURLOnly, true);
    },
    
    /**
      * Saves a form from a reference to the form
      * @param thisForm The name of the form to save
      * @param accountName The name to save the account as
      * @param fields A JSON object containing field data
      * @param currentURLOnly True if the account should only apply to the current URL, otherwise false
      * @param submitAfter True if the form should be submitted after saving, otherwise false
      */
    saveFormByRef : function (thisForm, accountName, fields, currentURLOnly, submitAfter) {
        var listener = {
            finished : function (data) {
                if (data.length > 0) {
                    alert(data);
                }
                if(submitAfter) {
                    LockCrypt.submitForm(thisForm);
                }
            }
        };
        var encodedFields = [];
        var curField;
        for(var i=0;i<fields.length;i++) {
            curField = fields[i];
            encodedFields[i] = {
                name : encodeURIComponent(curField.name),
                value : encodeURIComponent(curField.value),
                include : curField.include
            };
        }
        var data = {
            "type" : "SAVE",
            "Name" : encodeURIComponent(accountName),
            "URL" : (currentURLOnly ? window.content.location.href : ""),
            "Fields" : encodedFields
        };
        dump(data);
        thisForm.removeEventListener("submit", LockCrypt.doSave, true);
        this.sendReceive('localhost',1556,JSON.stringify(data),listener);
    },
    
    /**
      * Excludes the current site.
      */
    excludeCurrentSite : function () {
        LockCrypt.excludeSite(window.content.location.href);
    },
    
    /**
      * Excludes a site if it is not already excluded.
      * @param url The URL to exclude
      */
    excludeSite : function (url) {
        if (!LockCrypt.siteIsExcluded(url)) {
            var uri = this.parseUri(url);
            var siteToExclude;
            if (uri.protocol.toLowerCase() === "file") {
                siteToExclude = uri.protocol + "://" +uri.path;
            } else {
                siteToExclude = uri.protocol + "://" +uri.authority;
            }
            LockCrypt.excludeList[LockCrypt.excludeList.length] = siteToExclude;
            LockCrypt.prefs.setCharPref("excludeList", JSON.stringify(LockCrypt.excludeList));
        }
    },
    
    parseUri : function (sourceUri) {
        // Credits to http://badassery.blogspot.com
        var options = {
            	strictMode: false,
            	key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
            	q:   {
            		name:   "queryKey",
            		parser: /(?:^|&)([^&=]*)=?([^&]*)/g
            	},
            	parser: {
            		strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
            		loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
            	}
        };
    	var	m   = options.parser[options.strictMode ? "strict" : "loose"].exec(sourceUri);
    	var uri = {},
    		i   = 14;
    	while (i--) {
    	    uri[options.key[i]] = m[i] || "";
    	}
    
    	uri[options.q.name] = {};
    	uri[options.key[12]].replace(options.q.parser, function ($0, $1, $2) {
    		if ($1) { uri[options.q.name][$1] = $2; }
    	});
    
    	return uri;
    },
    
    /**
      * Enables or disables LockCrypt
      * @param enabled True to enable, false to disable
      */
    setEnabled : function (enabled) {
        LockCrypt.enabled = enabled;
        LockCrypt.prefs.setBoolPref("enabled", enabled);
        var statusIcon = document.getElementById("lc-statusbar");
        if (LockCrypt.enabled) {
            if (LockCrypt.enableSave) {
                LockCrypt.assignSubmitListeners();
            } else {
                LockCrypt.removeSubmitListeners();
            }
            if (LockCrypt.autoFill) {
                LockCrypt.autoFillForms(); 
            }
            dump("LockCrypt loaded\n");
            statusIcon.setAttribute("src", "chrome://LockCrypt/skin/classic/LockCrypt.ico");
        } else {
            statusIcon.setAttribute("src", "chrome://LockCrypt/skin/classic/LockCrypt_Disabled.ico");
        }
    },
    
    /**
      * Enables or disables prompting to save forms
      * @param enabled True to enable, false to disable
      */
    setEnableSave : function (enableSave) {
        LockCrypt.enableSave = enableSave;
        LockCrypt.prefs.setBoolPref("enableSave", enableSave);
        var statusIcon = document.getElementById("lc-statusbar");
        if (LockCrypt.enabled && LockCrypt.enableSave) {
            LockCrypt.assignSubmitListeners();
            dump("LCFF: Form saving enabled\n");
        } else {
            LockCrypt.removeSubmitListeners();
        }
    },
    
    /**
      * Toggles autofill on/off
      * @param autoFill True to enable autofill, otherwise false
      */
    setAutoFill : function (autoFill) {
        this.autoFill = !this.autoFill;
        this.prefs.setBoolPref("autoFill", autoFill);    
    },
    
    /**
      * Returns the user preference for filling only empty fields
      */
    getOnlyEmpty : function () {
        return this.prefs.getBoolPref("onlyEmpty");
    },
    
    /**
      * Returns true if the current site is in the exclude list, otherwise returns false
      */
    currentSiteIsExcluded : function () {
        return LockCrypt.siteIsExcluded(window.content.location.href);
    },
    
    /**
      * Returns true if the specified site is in the exclude list, otherwise returns false
      * @param url The URL to check
      */
    siteIsExcluded : function (url) {
        for(var i=0;i<LockCrypt.excludeList.length;i++) {
            var site = LockCrypt.excludeList[i];
            if (site.length > 2 && url.indexOf(site) !== -1) {
                //dump("Site excluded - '" +window.content.location.href + "' contains '"+ LockCrypt.excludeList[i]+ "' at '" +window.content.location.href.indexOf(LockCrypt.excludeList[i])+ "\n");
                return true;
            }
        }
        return false;
    },
    
    /**
      * Sends data then notifies a listener when data is received
      * @param host The host to send to
      * @param port The port
      * @param outputData The data to send
      * @param listener The listener to notify on receipt of data (should implement a 'finished(data)' method
      */
    sendReceive : function (host,port,outputData,listener) {
        try {
            var transportService = Components.classes["@mozilla.org/network/socket-transport-service;1"]
                                             .getService(Components.interfaces.nsISocketTransportService);
            var transport = transportService.createTransport(null,0,host,port,null);
        
            var outstream = transport.openOutputStream(transport.OPEN_BLOCKING,0,0);
            var dataToSend = outputData+String.fromCharCode(23);
        
            var stream = transport.openInputStream(0,0,0);
            var instream = Components.classes["@mozilla.org/scriptableinputstream;1"]
                                     .createInstance(Components.interfaces.nsIScriptableInputStream);
            instream.init(stream);
            outstream.write(dataToSend,dataToSend.length);
            outstream.close();
            var dataListener = {
                data : "",
                onStartRequest: function (request, context){},
                onStopRequest: function (request, context, status){
                    instream.close();
                    listener.finished(this.data);
                },
                onDataAvailable: function (request, context, inputStream, offset, count){
                    this.data += instream.read(count);
                }
            };
            
            var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
                                 .createInstance(Components.interfaces.nsIInputStreamPump);
            pump.init(stream, -1, -1, 0, 0, false);
            pump.asyncRead(dataListener,null);
            //dump("\n\n\nSent:" +outputData+ "\n");
          } catch (ex){
            dump("Ex: " +ex+ "\n");
            return ex;
          }
          return null;
    },
    
    /**
      * Opens a link in a new tab 
      * @param url The URL to open
      */
    openLink : function (url) {
        var win = Components.classes['@mozilla.org/appshell/window-mediator;1']
                            .getService(Components.interfaces.nsIWindowMediator)
                            .getMostRecentWindow('navigator:browser');
        win.openUILinkIn(url, 'tab');
    },
    
    /**
      * Finds the form contaning the specified element 
      * @param element The element whose parent form to locate
      */
    findForm : function (element) {
        if (element.tagName.toLowerCase() === 'form') {
            return element;
        } else {
            return LockCrypt.findForm(element.parentNode);
        }
    },
    
    /**
      * Finds a form by name 
      * @param formName The name of the form to find
      * @return A reference to the form or null if it could not be found
      */
    getFormByName : function (formName) {
        var forms = content.document.forms;
        var thisForm = null;
        for(var i=0;i<forms.length;i++) {
            if (forms[i].name === formName) {
                thisForm = forms[i];
                break;
            }
        }
        return thisForm;
    },
    
    /**
      * Tries to find an identifier for the specified form (either the name or id attribute)
      * @param form The form to identify
      * @return An identifier or null if both the name and id attributes are blank
      */
    getFormId : function (form) {
        if(typeof(form.name) !== 'undefined' && form.name.length > 0) {
            return form.name;
        } else if(typeof(form.id) !== 'undefined' && form.id.length > 0) {
            return form.id;
        } else {
            return null;
        }
    },
    
    /**
      * Returns true if the array (haystack) contains needle, otherwise false 
      * @param needle The item to find
      * @param haystack The array to search
      */
    arrayContains : function (needle, haystack) {
        if ((typeof needle === 'undefined') || (typeof haystack === 'undefined')) {
            return false;
        } else {
            for(var i=0;i<haystack.length;i++) {
                if (haystack[i] === needle) {
                    return true;
                }
            }
        }
    },
    
    /**
      * Finds the label for an element
      */
    findLabel : function (form, element) {
        var labels = form.getElementsByTagName("label"),
            thisLabel;
        for(var i=0;i<labels.length;i++) {
            thisLabel = labels[i];
            if (thisLabel.htmlFor == element.id && element.parentNode !== thisLabel) {
                return thisLabel.innerHTML;
            }
        }
        if (element.name.length > 0) {
            return element.name.substring(0,1).toUpperCase()+ element.name.substring(1);
        }
        return false;
    }
};
String.prototype.trim = function () {
	return this.replace(/^\s+|\s+$/g,"");
};
window.addEventListener("load", function (e) { LockCrypt.onLoad(); }, false);
window.addEventListener("unload", function (e) { LockCrypt.onUnload(); }, false);